import numpy as np

def calculate_iou(box1, box2):
    """
    计算两个边界框的IoU (Intersection over Union)。
    box: [x1, y1, x2, y2]
    """
    # 提取坐标
    x1_b1, y1_b1, x2_b1, y2_b1 = box1
    x1_b2, y1_b2, x2_b2, y2_b2 = box2

    # 计算交集区域的坐标
    x1_inter = max(x1_b1, x1_b2)
    y1_inter = max(y1_b1, y1_b2)
    x2_inter = min(x2_b1, x2_b2)
    y2_inter = min(y2_b1, y2_b2)

    # 计算交集区域的面积
    # 如果没有交集，宽度或高度将小于0，面积为0
    inter_width = max(0, x2_inter - x1_inter)
    inter_height = max(0, y2_inter - y1_inter)
    inter_area = inter_width * inter_height

    # 计算两个边界框的面积
    area_b1 = (x2_b1 - x1_b1) * (y2_b1 - y1_b1)
    area_b2 = (x2_b2 - x1_b2) * (y2_b2 - y1_b2)

    # 计算并集面积
    union_area = area_b1 + area_b2 - inter_area

    # 避免除以零
    if union_area == 0:
        return 0.0
    
    iou = inter_area / union_area
    return iou

def calculate_giou(box1, box2):
    """
    计算两个边界框的GIoU (Generalized Intersection over Union)。
    box: [x1, y1, x2, y2]
    """
    iou = calculate_iou(box1, box2)

    # 提取坐标
    x1_b1, y1_b1, x2_b1, y2_b1 = box1
    x1_b2, y1_b2, x2_b2, y2_b2 = box2

    # 计算最小外接矩形C的坐标
    x1_c = min(x1_b1, x1_b2)
    y1_c = min(y1_b1, y1_b2)
    x2_c = max(x2_b1, x2_b2)
    y2_c = max(y2_b1, y2_b2)

    # 计算C的面积
    area_c = (x2_c - x1_c) * (y2_c - y1_c)

    # 计算两个边界框的并集面积
    # 这里需要重新计算并集，因为IoU的计算中可能已经处理了无交集的情况
    area_b1 = (x2_b1 - x1_b1) * (y2_b1 - y1_b1)
    area_b2 = (x2_b2 - x1_b2) * (y2_b2 - y1_b2)
    
    # 为了正确计算union_area，需要再次计算交集面积（即使为0）
    x1_inter = max(x1_b1, x1_b2)
    y1_inter = max(y1_b1, y1_b2)
    x2_inter = min(x2_b1, x2_b2)
    y2_inter = min(y2_b1, y2_b2)
    inter_width = max(0, x2_inter - x1_inter)
    inter_height = max(0, y2_inter - y1_inter)
    inter_area = inter_width * inter_height
    
    union_area = area_b1 + area_b2 - inter_area

    # 计算C中除了A和B并集以外的区域面积 (空隙面积)
    # 避免area_c为0，尤其当box1和box2是点或者线时
    if area_c == 0:
        return -1.0 # 或者其他表示错误/未定义的值
    
    giou = iou - (area_c - union_area) / area_c
    return giou

if __name__ == "__main__":
    # 场景1：两个框完全重合
    box_a = [0, 0, 10, 10]
    box_b = [0, 0, 10, 10]
    iou_ab = calculate_iou(box_a, box_b)
    giou_ab = calculate_giou(box_a, box_b)
    print(f"场景1：框A {box_a}, 框B {box_b}")
    print(f"IoU: {iou_ab:.4f}, GIoU: {giou_ab:.4f}\n") # IoU和GIoU都为1

    # 场景2：两个框部分重叠
    box_c = [0, 0, 10, 10]
    box_d = [5, 5, 15, 15]
    iou_cd = calculate_iou(box_c, box_d)
    giou_cd = calculate_giou(box_c, box_d)
    print(f"场景2：框C {box_c}, 框D {box_d}")
    print(f"IoU: {iou_cd:.4f}, GIoU: {giou_cd:.4f}\n") # IoU会有一个值，GIoU会略小于IoU

    # 场景3：两个框没有交集，但挨得很近
    box_e = [0, 0, 5, 5]
    box_f = [6, 0, 10, 5] # 相距1个单位
    iou_ef = calculate_iou(box_e, box_f)
    giou_ef = calculate_giou(box_e, box_f)
    print(f"场景3：框E {box_e}, 框F {box_f} (挨得很近)")
    print(f"IoU: {iou_ef:.4f}, GIoU: {giou_ef:.4f}\n") # IoU为0，GIoU为负值，且能区分距离

    # 场景4：两个框没有交集，且离得很远
    box_g = [0, 0, 5, 5]
    box_h = [100, 100, 105, 105] # 相距很远
    iou_gh = calculate_iou(box_g, box_h)
    giou_gh = calculate_giou(box_g, box_h)
    print(f"场景4：框G {box_g}, 框H {box_h} (离得很远)")
    print(f"IoU: {iou_gh:.4f}, GIoU: {giou_gh:.4f}\n") # IoU为0，GIoU为负值，且比场景3的GIoU更小

    # 场景5：一个框包含另一个框
    box_i = [0, 0, 20, 20]
    box_j = [5, 5, 15, 15]
    iou_ij = calculate_iou(box_i, box_j)
    giou_ij = calculate_giou(box_i, box_j)
    print(f"场景5：框I {box_i} (大), 框J {box_j} (小，被包含)")
    print(f"IoU: {iou_ij:.4f}, GIoU: {giou_ij:.4f}\n") # IoU会有值，GIoU会更高